﻿using CLVisionModbus;
using IniParser.Model;
using IniParser;
using Microsoft.Win32;
using NationalInstruments.TestStand.Interop.API;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CLCommuModbus
{
    public partial class CommuCaller
    {

        internal void WriteToPLC(int startAddress, DataType dataType, object value, ModbusDataType modbusDataType, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            //PLC地址为0时，代码中传入值要加1
            startAddress = startAddress + 1;
            CLVisionModbusSlave slave = cLVisionModbusSlave;
            try
            {
                switch (modbusDataType)
                {
                    case ModbusDataType.Coil:
                        slave.WriteSingleBoolToCoil(startAddress, (bool)value, out ErrorCode, out ErrorText);
                        break;
                    case ModbusDataType.InputDiscretes:
                        slave.WriteSingleBoolToDiscrete(startAddress, (bool)value, out ErrorCode, out ErrorText);
                        break;
                    case ModbusDataType.InputRegisters:
                        switch (dataType)
                        {
                            case DataType.USHORT:
                                slave.WriteSingleUshortToInputRegisters(startAddress, Convert.ToUInt16(value), out ErrorCode, out ErrorText);
                                break;
                            case DataType.INT:
                                slave.WriteSingleInt32ToInputRegisters(startAddress, Convert.ToInt32(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.FLOAT:
                                slave.WriteSingleFloatToInputRegisters(startAddress, Convert.ToSingle(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.DOUBLE:
                                slave.WriteSingleDoubleToInputRegisters(startAddress, Convert.ToDouble(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.STRING:
                                //string开40个地址存放数据
                                int count = Encoding.UTF8.GetByteCount(value.ToString() + "*&");
                                if (count > (int)ObjectHandShakeMode2.ObjectStringDataLength * 2)
                                {
                                    string errMes = $"Error:The character length is more than {(int)ObjectHandShakeMode2.ObjectStringDataLength * 2} bytes!" + "*&";
                                    slave.WriteSingleStringToInputRegisters(startAddress, errMes, out ErrorCode, out ErrorText);
                                }
                                else
                                {
                                    //写入字符串
                                    value = value.ToString() + "*&";
                                    slave.WriteSingleStringToInputRegisters(startAddress, value.ToString(), out ErrorCode, out ErrorText);
                                }
                                break;
                            default:
                                ErrorCode = -1;
                                ErrorText = "DataType有误！";
                                break;
                        }
                        break;
                    case ModbusDataType.HoldingRegisters:
                        switch (dataType)
                        {
                            case DataType.USHORT:
                                slave.WriteSingleUshortToHoldingRegisters(startAddress, Convert.ToUInt16(value), out ErrorCode, out ErrorText);
                                break;
                            case DataType.INT:
                                slave.WriteSingleInt32ToHoldingRegisters(startAddress, Convert.ToInt32(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.FLOAT:
                                slave.WriteSingleFloatToHoldingRegisters(startAddress, Convert.ToSingle(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.DOUBLE:
                                slave.WriteSingleDoubleToHoldingRegisters(startAddress, Convert.ToDouble(value), out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.STRING:
                                //string开40个地址存放数据
                                int count = Encoding.UTF8.GetByteCount(value.ToString() + "*&");
                                if (count > (int)ObjectHandShakeMode2.ObjectStringDataLength * 2)
                                {
                                    string errMes = $"Error:The character length is more than {(int)ObjectHandShakeMode2.ObjectStringDataLength * 2} bytes!" + "*&";
                                    slave.WriteSingleStringToHoldingRegisters(startAddress, errMes, out ErrorCode, out ErrorText);
                                }
                                else
                                {
                                    //写入字符串
                                    value = value.ToString() + "*&";
                                    slave.WriteSingleStringToHoldingRegisters(startAddress, value.ToString(), out ErrorCode, out ErrorText);
                                }
                                break;
                            default:
                                ErrorCode = -1;
                                ErrorText = "DataType有误！";
                                break;
                        }
                        break;
                    default:
                        ErrorCode = -1;
                        ErrorText = "ModbusDataType有误！";
                        break;
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "向PLC写入数据失败！" + ex.Message;
            }
        }
        internal object ReadFromPLC(int startAddress, DataType dataType, ModbusDataType modbusDataType, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            //PLC地址为0时，代码中传入值要加1
            startAddress = startAddress + 1;
            CLVisionModbusSlave slave = cLVisionModbusSlave;
            try
            {
                object data = null;
                switch (modbusDataType)
                {
                    case ModbusDataType.Coil:
                        data = slave.ReadSingleBoolFromCoil(startAddress, out ErrorCode, out ErrorText);
                        break;
                    case ModbusDataType.InputDiscretes:
                        data = slave.ReadSingleBoolFromDiscrete(startAddress, out ErrorCode, out ErrorText);
                        break;
                    case ModbusDataType.InputRegisters:
                        switch (dataType)
                        {
                            case DataType.USHORT:
                                data = slave.ReadSingleUshortFromInputRegisters(startAddress, out ErrorCode, out ErrorText);
                                break;
                            case DataType.INT:
                                data = slave.ReadSingleInt32FromInputRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.FLOAT:
                                data = slave.ReadSingleFloatFromInputRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.DOUBLE:
                                data = slave.ReadSingleDoubleFromInputRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.STRING:
                                //string开40个地址存放数据
                                data = slave.ReadSingleStringFromInputRegisters(startAddress, (int)ObjectHandShakeMode2.ObjectStringDataLength, out ErrorCode, out ErrorText);
                                //判断字符串是否有结束符"*&"，将结束符清除
                                int index = data.ToString().IndexOf("*&");
                                if (index >= 0)
                                {
                                    data = data.ToString().Substring(0, index);
                                }
                                else
                                {
                                    data = "";
                                }
                                break;
                            default:
                                ErrorCode = -1;
                                ErrorText = "DataType有误！";
                                break;
                        }
                        break;
                    case ModbusDataType.HoldingRegisters:
                        switch (dataType)
                        {
                            case DataType.USHORT:
                                data = slave.ReadSingleUshortFromHoldingRegisters(startAddress, out ErrorCode, out ErrorText);
                                break;
                            case DataType.INT:
                                data = slave.ReadSingleInt32FromHoldingRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.FLOAT:
                                data = slave.ReadSingleFloatFromHoldingRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.DOUBLE:
                                data = slave.ReadSingleDoubleFromHoldingRegisters(startAddress, out ErrorCode, out ErrorText, DataFormat.Default);
                                break;
                            case DataType.STRING:
                                //string开40个地址存放数据
                                data = slave.ReadSingleStringFromHoldingRegisters(startAddress, (int)ObjectHandShakeMode2.ObjectStringDataLength, out ErrorCode, out ErrorText);
                                //判断字符串是否有结束符"*&"，将结束符清除
                                int index = data.ToString().IndexOf("*&");
                                if (index >= 0)
                                {
                                    data = data.ToString().Substring(0, index);
                                }
                                else
                                {
                                    data = "";
                                }
                                break;
                            default:
                                ErrorCode = -1;
                                ErrorText = "DataType有误！";
                                break;
                        }
                        break;
                    default:
                        ErrorCode = -1;
                        ErrorText = "ModbusDataType有误！";
                        break;
                }
                return data;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "从PLC读取数据失败！" + ex.Message;
                return null;
            }
        }
        public bool CreateConnect(string ip, int port, out CLVisionModbusSlave slave, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            slave = null;
            try
            {
                CLVisionModbusSlave cLVisionModbusSlave = new CLVisionModbusSlave();
                bool ret = cLVisionModbusSlave.CreateTcpSlave(out ErrorCode, out ErrorText, ip, port);
                if (ErrorCode == -1)
                {
                    ErrorText = $"初始化Slave失败,ip:{ip},port:{port}：{ErrorText}";
                }
                slave = cLVisionModbusSlave;
                return ret;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "创建Slav通讯连接失败！" + ex.Message;
                slave = null;
                return false;
            }
        }
        public string ReadIniFile(string configFilePath, string sectionName, string key, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                // 使用FileIniDataParser实例解析或保存ini文件。
                var parser = new FileIniDataParser();

                // 加载INI文件，读取实例中包含的数据，并解析该数据
                IniData data = parser.ReadFile(configFilePath);
                return data[sectionName][key];
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "读取本地ini文件失败！" + ex.Message;
                return null;
            }
        }

        private bool CreateObjectStructInTS(int threadCount, PropertyObject arrayContainerToReceiveConfig, string configFilePath, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                string str = ReadFile(configFilePath, out ErrorCode, out ErrorText);
                if (ErrorCode == -1)
                {
                    return false;
                }
                //读取自定义参数
                JObject jb_Object = JsonConvert.DeserializeObject(str) as JObject;

                arrayContainerToReceiveConfig.DeleteElements(0, arrayContainerToReceiveConfig.GetNumElements());

                arrayContainerToReceiveConfig.SetNumElements(threadCount, 0);

                for (int i = 0; i < threadCount; i++)
                {
                    //获取数组元素
                    PropertyObject propertyElement = arrayContainerToReceiveConfig.GetPropertyObjectByOffset(i, 0);
                    //设置名称
                    // propertyElement.Name = "Thread" + i.ToString();
                    //将本地参数写入TS
                    WriteObjectStructToTS(jb_Object, propertyElement, out ErrorCode, out ErrorText);
                    if (ErrorCode == -1)
                    {
                        return false;
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "TestStand中创建Objects失败！" + ex.Message;
                throw;
            }
        }
        public void WriteObjectStructToTS(JObject jb, PropertyObject strucVar, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                foreach (var item in jb)
                {
                    if (item.Key.ToLower() == "root")
                    {
                        continue;
                    }
                    bool canValid;
                    PropertyObject currentStrucVar;
                    //判断是否可以创建层级
                    strucVar.ValidateNewSubPropertyName(item.Key, false, out canValid);
                    if (canValid)
                    {
                        //TS中创建新的子级
                        strucVar.NewSubProperty(item.Key, PropertyValueTypes.PropValType_Container, false, "", 0);
                    }
                    currentStrucVar = strucVar.GetPropertyObject(item.Key, 0);

                    JObject jbitem = JsonConvert.DeserializeObject(item.Value.ToString()) as JObject;

                    //遍历子节点Object
                    foreach (var item2 in jbitem)
                    {
                        string objectName = "";
                        int objectID = -1;

                        JObject jbitem1 = JsonConvert.DeserializeObject(item2.Value.ToString()) as JObject;
                        //Object具体内容
                        foreach (var itemObj in jbitem1)
                        {
                            //含"#"的为多国语言参数,不显示在TS中
                            if (itemObj.Key.Contains("#"))
                            {
                                continue;
                            }
                            if (itemObj.Key.ToLower() == "id")
                            {
                                objectID = Convert.ToInt32(itemObj.Value);

                            }
                            else if (itemObj.Key.ToLower() == "name")
                            {
                                objectName = itemObj.Value.ToString();
                            }

                            else  //存储数据层
                            {

                                string IDName = "ID" + objectID + "_" + objectName;
                                //判断是否可以创建层级
                                currentStrucVar.ValidateNewSubPropertyName(IDName, false, out canValid);
                                if (canValid)
                                {
                                    //TS中创建新的子级
                                    currentStrucVar.NewSubProperty(IDName, PropertyValueTypes.PropValType_Container, false, "", 0);
                                }
                                currentStrucVar = currentStrucVar.GetPropertyObject(IDName, 0);




                                //判断是否可以创建层级
                                currentStrucVar.ValidateNewSubPropertyName(itemObj.Key, false, out bool canValid_Input2);
                                if (canValid_Input2)
                                {
                                    //TS中创建新的子级
                                    currentStrucVar.NewSubProperty(itemObj.Key, PropertyValueTypes.PropValType_Container, false, "", 0);
                                }
                                currentStrucVar = currentStrucVar.GetPropertyObject(itemObj.Key, 0);
                                JObject jbProperty = JsonConvert.DeserializeObject(itemObj.Value.ToString()) as JObject;

                                string dataType = "";
                                bool isArray = false;

                                foreach (var item3 in jbProperty)
                                {
                                    //含"#"的为多国语言参数,不显示在TS中
                                    if (item3.Key.Contains("#"))
                                    {
                                        continue;
                                    }
                                    //数据类型
                                    if (item3.Key.ToLower() == "datatype")
                                    {
                                        dataType = item3.Value.ToString().ToUpper();
                                    }
                                    //数据是否为数组 
                                    else if (item3.Key.ToLower() == "isarray")
                                    {
                                        isArray = Convert.ToInt32(item3.Value) == 1 ? true : false;
                                    }
                                    else if (item3.Key.ToLower() == "arraylength" && isArray)
                                    {

                                        /* 暂未未找到直接修改节点数据类型的方法，如可以修改数据类型，则直接第3步，再修改数据类型  */
                                        //1、获取父节点
                                        PropertyObject propertyObject_str = currentStrucVar.Parent;
                                        string proName = currentStrucVar.Name;
                                        //2、删除指定的containter子节点
                                        propertyObject_str.DeleteSubProperty(proName, 0);
                                        //3、重新创建指定数据类型子节点
                                        PropertyValueTypes propertyValueType = PropertyValueTypes.PropValType_Container;

                                        DataType type = (DataType)Enum.Parse(typeof(DataType), dataType);
                                        switch (type)
                                        {
                                            case DataType.USHORT:
                                                propertyValueType = PropertyValueTypes.PropValType_Number;
                                                break;
                                            case DataType.INT:
                                                propertyValueType = PropertyValueTypes.PropValType_Number;
                                                break;
                                            case DataType.FLOAT:
                                                propertyValueType = PropertyValueTypes.PropValType_Number;
                                                break;
                                            case DataType.DOUBLE:
                                                propertyValueType = PropertyValueTypes.PropValType_Number;
                                                break;
                                            case DataType.STRING:
                                                propertyValueType = PropertyValueTypes.PropValType_String;
                                                break;
                                            default:
                                                break;
                                        }
                                        if (Convert.ToInt32(item3.Value) > 1)
                                        {
                                            propertyObject_str.NewSubProperty(proName, propertyValueType, true, "", 0);
                                            PropertyObject current = propertyObject_str.GetPropertyObject(proName, 0);

                                            //设置数组长度
                                            current.SetNumElements(System.Convert.ToInt32(item3.Value));
                                        }
                                        else
                                        {
                                            propertyObject_str.NewSubProperty(proName, propertyValueType, false, "", 0);
                                        }
                                    }
                                }

                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "在TestStand中写入Object失败！" + ex.Message;
            }
        }
        public string ReadFile(string filePath, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            string result = null;
            try
            {
                using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    using (StreamReader streamReader = new StreamReader(fs, Encoding.GetEncoding("GB2312")))
                    {
                        result = streamReader.ReadToEnd();
                    }
                }
                return result;
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "读取本地文件失败！" + ex.Message;
                return result;
            }
        }

        internal void WriteUshortsToPLC(int startAddress, ushort[] value, ModbusDataType modbusDataType, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            startAddress = startAddress + 1;
            try
            {
                switch (modbusDataType)
                {
                    case ModbusDataType.InputRegisters:
                        cLVisionModbusSlave.WriteMultiUshortToInputRegisters(startAddress, value, out ErrorCode, out ErrorText);
                        return;
                    case ModbusDataType.HoldingRegisters:
                        cLVisionModbusSlave.WriteMultiUshortToHoldingRegisters(startAddress, value, out ErrorCode, out ErrorText);
                        return;
                    default:
                        ErrorCode = -1;
                        ErrorText = $"ModbusDataType参数{modbusDataType}有误或不支持！";
                        return;
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "向PLC写入ushort数组失败！" + ex.Message;
                return;
            }
        }
        internal object ReadUshortsFromPLC(int startAddress, int length, ModbusDataType modbusDataType, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            ushort[] ushorts = null;
            startAddress = startAddress + 1;
            try
            {
                switch (modbusDataType)
                {
                    case ModbusDataType.InputRegisters:
                        ushorts = cLVisionModbusSlave.ReadMultiUshortFromInputRegisters(startAddress, length, out ErrorCode, out ErrorText);
                        return ushorts;
                    case ModbusDataType.HoldingRegisters:
                        ushorts = cLVisionModbusSlave.ReadMultiUshortFromHoldingRegisters(startAddress, length, out ErrorCode, out ErrorText);
                        return ushorts;
                    default:
                        ErrorCode = -1;
                        ErrorText = $"ModbusDataType参数{modbusDataType}有误或不支持！";
                        return ushorts;
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "从PLC读取ushort数组失败！" + ex.Message;
                return ushorts;
            }
        }
    }
}
